/*
 * File:    Logger.java
 * Created: 29-Nov-2007
 *
 * by Kenji Hollis <kenji@bitgatesoftware.com>
 * Released under the Creative Commons License version 2.5
 */

package com.bitgate.util.service.protocol.http.logger;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

import com.bitgate.util.service.protocol.http.HttpClientContext;
import com.bitgate.util.shutdown.ShutdownHook;
import com.bitgate.util.shutdown.ShutdownHookInterface;

import static com.bitgate.util.debug.Debug.debug;
import static com.bitgate.util.debug.Debug.isDebugEnabled;
import static com.bitgate.util.debug.Debug.warning;

/**
 * This class is responsible for logging requests made to the HTTP protocol service, which is why it is part of the
 * HTTP protocol jar file.  This class takes the logging method and directory for the specified HTTP context, and
 * writes the logs to that directory (or to the screen), depending on the logging method chosen.
 * 
 * @author kenji
 * @since 0.7.0
 */
public class Logger implements ShutdownHookInterface {
	
	private final static ConcurrentHashMap<String, FileOutputStream> loggerStreams = 
		new ConcurrentHashMap<String, FileOutputStream>();
	private volatile boolean isShutdown = false;
	
	static {
		ShutdownHook.getInstance().register(Logger.class);
	}
	
	/**
	 * This function logs the request made by the <code>HttpClientContext</code> object.  This must only be performed at 
	 * <code>WriteFuture onComplete</code> time, when the write operation completes.
	 * 
	 * @param hcContext <code>HttpClientContext</code> currently active for the connected client.
	 * @param byteCount <code>int</code> containing the number of bytes that were written to the endpoint client.
	 */
	public static void logRequest(HttpClientContext hcContext, int byteCount) {
		String logMethod = hcContext.getProperty("logger.method");
		String logLocation = hcContext.getProperty("logger.directory");
		
		if (logMethod.equalsIgnoreCase("screen")) {
			logToScreen(hcContext, byteCount);
		} else if (logMethod.equalsIgnoreCase("file")) {
			FileOutputStream foStream = loggerStreams.get(logLocation);
			
			if (foStream == null) {
				try {
					(new File(logLocation)).mkdirs();
					loggerStreams.put(logLocation, new FileOutputStream(new File(logLocation + "access_log")));
				} catch(IOException e) {
					if (isDebugEnabled()) {
						debug("Unable to open new access log at '" + logLocation + "': " + e.getMessage());
					}
					
					logToScreen(hcContext, byteCount);
					return;
				}
			}
			
			logToFile(hcContext, byteCount);
		} else {
			warning("Unknown logger method '" + logMethod + "' specified for logging request.");
		}
	}
	
	private static String createLogMessage(HttpClientContext hcContext, int byteCount) {
		return(hcContext.getClientContext().getClientChannel().getSocketChannel().socket().getLocalAddress().getHostAddress()
				+ " - - [" + new Date() + "] \"" + hcContext.getOriginalRequest() + "\" " +
				hcContext.getRequestResult().getResult() + " " + byteCount);
	}
	
	private synchronized static void logToScreen(HttpClientContext hcContext, int byteCount) {
		if (isDebugEnabled()) {
			debug(createLogMessage(hcContext, byteCount));
		}
	}
	
	private synchronized static void logToFile(HttpClientContext hcContext, int byteCount) {
		String logLocation = hcContext.getProperty("logger.directory");
		FileOutputStream foStream = loggerStreams.get(logLocation);
		String logMessage = createLogMessage(hcContext, byteCount) + "\n";
		
		try {
			foStream.write(logMessage.getBytes());
			
			if (isDebugEnabled()) {
				debug("Wrote log message to disk.");
			}
		} catch(IOException e) {
			if (isDebugEnabled()) {
				debug("(Unable to write log to disk): " + logMessage);
			}
		}
	}

	public boolean isShutdown() {
		return isShutdown;
	}

	public void shutdown() {
		if (isDebugEnabled()) {
			debug("Shutting down logging facilities.");
		}
		
		for(String logLocation : loggerStreams.keySet()) {
			try {
				loggerStreams.get(logLocation).close();
				if (isDebugEnabled()) {
					debug("Closed logger for '" + logLocation + "'");
				}
			} catch(IOException e) {
				if (isDebugEnabled()) {
					debug("Unable to close logger for '" + logLocation + "': " + e.getMessage());
				}
			}
		}
		
		if (isDebugEnabled()) {
			debug("All log locations closed.");
		}
	}
	
}